package generic.genetic;

public class GeneticAlgorithm {

	private Population population;
	private int max_num_generations;
	private int numGeneration;
	private double crossover_rate;
	private double mutation_rate;
	private double elitism_rate;
	private boolean elitism_selection;
	private boolean maximize_problem;
	private Chromosome bestChromosome;
	
	private int selection_type;
	private int crossover_type;
	
	public int firstGenBestChrom;	
	public double[] bestEver;
	public double[] bestGen;
	public double[] avgGen;
	
	public GeneticAlgorithm(Population p){
		population = p;
		
		max_num_generations = 100;
		numGeneration = 0;
		crossover_rate = 0.6;
		mutation_rate = 0.02;
		elitism_rate = 0.02;
		maximize_problem = true;		
		firstGenBestChrom = 0;
		
		elitism_selection = false;
		selection_type = 0;
		
		bestEver = new double[max_num_generations];
		bestGen = new double[max_num_generations];
		avgGen = new double[max_num_generations];
	}
	
	public void run(){
		numGeneration = 0;
		population.initialize();
		population.evaluate(maximize_problem);
		bestChromosome = population.getBestChromosome();
		Chromosome[] elite = null;
		while (numGeneration < max_num_generations){
			numGeneration++;
			//Save elite
			if (elitism_selection) elite = population.getNBest((int) (population.size*elitism_rate+1));
			population.selection(selection_type);
			population.reproduction(crossover_type, crossover_rate);
			population.mutation(mutation_rate);
			//Restore elite
			if (elitism_selection) population.replace(elite);
			population.evaluate(maximize_problem);
			if (maximize_problem){
				if (population.getBestChromosome().evaluate() > bestChromosome.evaluate()){
					bestChromosome = population.getBestChromosome().getCopy();
					firstGenBestChrom = numGeneration-1;
				}				
			}else{
				if (population.getBestChromosome().evaluate() < bestChromosome.evaluate()){
					bestChromosome = population.getBestChromosome().getCopy();
					firstGenBestChrom = numGeneration-1;
				}				
			}
			bestEver[numGeneration-1] = bestChromosome.evaluate();
			bestGen[numGeneration-1] = population.getBestChromosome().evaluate();
			avgGen[numGeneration-1] = population.avg();
			//System.out.println(numGeneration + ":\t"+population.getBestChromosome() + "\t---\t" + bestChromosome);
			//System.out.println(numGeneration + ":\t"+population.getBestChromosome().evaluate() + "\t---\t" + bestChromosome.evaluate());
		}
	}
		
	public int getMax_num_generations() { return max_num_generations; }
	public double getCrossover_rate() { return crossover_rate; }
	public double getMutation_rate() { return mutation_rate; }
	public double getElitism_rate() { return elitism_rate; }
	public int getPopulation_size() { return population.getSize(); }
	
	public void setMax_num_generations(int max_num_generations) {
		this.max_num_generations = max_num_generations;
		bestEver = new double[max_num_generations];
		bestGen = new double[max_num_generations];
		avgGen = new double[max_num_generations];
	}
	public void setElitism_rate(double elitisim_rate) { this.elitism_rate = elitisim_rate; }
	public void setCrossover_rate(double crossover_rate) { this.crossover_rate = crossover_rate; }
	public void setMutation_rate(double mutation_rate) { this.mutation_rate = mutation_rate; }
	public void setPopulation_size(int population_size) { population.setSize(population_size); }
	
	public void setMaximizeProblem(boolean b) 	{ maximize_problem = b; }
	public void setElitism(boolean b)			{ elitism_selection = b; }
	
	public Population getPopulation() { return population; }

	public Chromosome getBestChromosome() {
		return bestChromosome;
	}

	public void setCrossover_method(int value) { crossover_type = value; }
	public void setSelection_method(int value) { selection_type = value; }

	public String getTitle() {
		String title = "";
		switch (selection_type) {
			case 0: title += "Ruleta"; break;
			case 1: title += "Torneo"; break;
		}
		
		if (elitism_selection) title += " con Elite (" + (int) (population.size*elitism_rate+1) +")";
		
		return title;
	}
}
